////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001.
// -------------------------------------------------------------------------
//  File name:   movie.h
//  Version:     v1.00
//  Created:     23/4/2002 by Timur.
//  Compilers:   Visual C++ 7.0
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#ifndef __movie_h__
#define __movie_h__

#if _MSC_VER > 1000
#pragma once
#endif

#include "IMovieSystem.h"
#include <CrySizer.h>

/**	This is descirption of currently playing sequence.
*/
struct PlayingSequence
{
	//! Sequence playnig.
	_smart_ptr<IAnimSequence> sequence;
	//! Start/End/Current playing time for this sequence.
	float startTime;
	float endTime;
	float currentTime;
	float currentSpeed;
	//! Sequence from other sequence's sequence track
	bool trackedSequence;
	bool bSingleFrame;

	void GetMemoryUsage(ICrySizer *pSizer) const
	{
		pSizer->AddObject(sequence);
	}

};

struct IConsoleCmdArgs;

struct ISkeletonAnim;

class CMovieSystem : public IMovieSystem
{
	typedef std::vector<PlayingSequence> PlayingSequences;

public:
	CMovieSystem( ISystem *system );
	~CMovieSystem();

	void Release() { delete this; };

	void SetUser(IMovieUser *pUser) { m_pUser=pUser; }
	IMovieUser* GetUser() { return m_pUser; }

	bool Load(const char *pszFile, const char *pszMission);

	ISystem* GetSystem() { return m_system; }

	IAnimTrack* CreateTrack( EAnimTrackType type );

	IAnimSequenceGroup* CreateSequenceGroup( const char *name, IAnimSequenceGroup *pParentGroup=NULL );
	void DeleteSequenceGroup( IAnimSequenceGroup *pGroup );
	IAnimSequenceGroup* FindSequenceGroup( const char *path ) const;

	IAnimSequence* CreateSequence( const char *sequence, IAnimSequenceGroup *pParentGroup=NULL, bool bLoad=false  );
	IAnimSequence* LoadSequence( const char *pszFilePath );
	IAnimSequence* LoadSequence( XmlNodeRef &xmlNode, bool bLoadEmpty=true );

	void RemoveSequence( IAnimSequence *seq );
	IAnimSequence* FindSequence( const char *sequence ) const;
	IAnimSequence* FindSequenceById( uint32 id ) const;
	IAnimSequence* GetSequence(int i) const;
	int GetNumSequences() const;
	IAnimSequence* GetPlayingSequence(int i) const;
	int GetNumPlayingSequences() const;

	IAnimSequenceGroup* GetRootGroup() const; 
	void SerializeSequenceGroups( XmlNodeRef &xmlNode, bool bLoading );

	int OnSequenceRenamed( const char *before, const char *after );
	int OnCameraRenamed( const char *before, const char *after );

	bool AddMovieListener(IAnimSequence* pSequence, IMovieListener* pListener);
	bool RemoveMovieListener(IAnimSequence* pSequence, IMovieListener* pListener);

	void RemoveAllSequences();

	//////////////////////////////////////////////////////////////////////////
	// Sequence playback.
	//////////////////////////////////////////////////////////////////////////
	void PlaySequence( const char *sequence,IAnimSequence *parentSeq=NULL,bool bResetFX=true,
											bool bTrackedSequence=false,float startTime = -FLT_MAX,float endTime = -FLT_MAX );
	void PlaySequence( IAnimSequence *seq,IAnimSequence *parentSeq=NULL,bool bResetFX=true,
											bool bTrackedSequence=false,float startTime = -FLT_MAX,float endTime = -FLT_MAX );
	void PlayOnLoadSequences();

	bool StopSequence( const char *sequence );
	bool StopSequence( IAnimSequence *seq);
	bool AbortSequence(IAnimSequence *seq, bool bLeaveTime=false);

	void StopAllSequences();
	void StopAllCutScenes();
	void Pause( bool bPause );

	void Reset( bool bPlayOnReset,bool bSeekAllToStart );
	void StillUpdate();
	void Update( float dt );
	void Render();

	// Due to the multi-threaded animation system, the movie system with animation tracks
	// cannot update ISkeletonAnims on spot and must buffer them for a later application.
	// This method does that application from queued commands.
	void ApplyQueuedSkelAnimCmds();
	void QueueStopAnimationsAllLayers(ISkeletonAnim *pSkelAnim);
	void QueueStartAnimation(ISkeletonAnim *pSkeletonAnim, const char *animName, const CryCharAnimationParams& animParams);
	void QueueManualSeekAnimationInFIFO(ISkeletonAnim *pSkelAnim, uint32 layer, uint32 animIndex, float time, bool bAnimEvents);
	void QueueSetLayerTime(ISkeletonAnim *pSkelAnim, uint32 layer, float time);
	void QueueSetTrackViewMixingWeight(ISkeletonAnim *pSkelAnim, uint32 layer, float weight);

	void StartCapture(const ICaptureKey& key);
	void EndCapture();
	void ControlCapture();

	bool IsPlaying( IAnimSequence *seq ) const;

	void Pause();
	void Resume();

	VIRTUAL void PauseCutScenes();
	VIRTUAL void ResumeCutScenes();

	void SetRecording( bool recording ) { m_bRecording = recording; };
	bool IsRecording() const { return m_bRecording; };

	void SetCallback( IMovieCallback *pCallback ) { m_pCallback=pCallback; }
	IMovieCallback* GetCallback() { return m_pCallback; }
	void Callback( IMovieCallback::ECallbackReason Reason,IAnimNode *pNode );

	void Serialize( XmlNodeRef &xmlNode,bool bLoading, bool bRemoveOldNodes=false, bool bLoadEmpty=true );

	const SCameraParams& GetCameraParams() const { return m_ActiveCameraParams; }
	void SetCameraParams( const SCameraParams &Params );

	void SendGlobalEvent( const char *pszEvent );
	void OnPlaySound( IAnimSequence* pSeq, ISound *pSound );
	void SetSequenceStopBehavior( ESequenceStopBehavior behavior );
	IMovieSystem::ESequenceStopBehavior GetSequenceStopBehavior();

	float GetPlayingTime(IAnimSequence * pSeq);
	bool SetPlayingTime(IAnimSequence * pSeq, float fTime);

	float GetPlayingSpeed(IAnimSequence * pSeq);
	bool SetPlayingSpeed(IAnimSequence * pSeq, float fTime);

	bool GetStartEndTime(IAnimSequence *pSeq,float &fStartTime,float &fEndTime);
	bool SetStartEndTime(IAnimSequence *pSeq,const float fStartTime,const float fEndTime);

	void GoToFrame( const char * seqName, float targetFrame );

	const char * GetOverrideCamName() const 
	{ return m_mov_overrideCam->GetString(); }

	VIRTUAL bool IsPhysicsEventsEnabled() const
	{ return m_bPhysicsEventsEnabled; }
	VIRTUAL void EnablePhysicsEvents(bool enable)
	{ m_bPhysicsEventsEnabled = enable; }

	VIRTUAL void EnableBatchRenderMode(bool bOn)
	{ m_bBatchRenderMode = bOn; }
	VIRTUAL bool IsInBatchRenderMode() const
	{ return m_bBatchRenderMode; }

	int GetEntityNodeParamCount() const;
	bool GetEntityNodeParamInfo( int nIndex, IAnimNode::SParamInfo& info ) const;

	void GetMemoryUsage( ICrySizer *pSizer ) const
	{
		pSizer->AddObject(this,sizeof(*this));
		pSizer->AddObject(m_sequences);
		pSizer->AddObject(m_playingSequences);
		pSizer->AddObject(m_movieListenerMap);
	}
protected:
	void NotifyListeners(IAnimSequence* pSequence, IMovieListener::EMovieEvent event);
	bool InternalStopSequence( IAnimSequence *seq, bool bIsAbort=false);
	bool FindSequence(IAnimSequence* sequence, PlayingSequences::const_iterator& sequenceIteratorOut) const;
	bool FindSequence(IAnimSequence* sequence, PlayingSequences::iterator& sequenceIteratorOut);

#if !defined(_RELEASE)
	static void GoToFrameCmd(IConsoleCmdArgs *pArgs);
	static void ListSequencesCmd(IConsoleCmdArgs *pArgs);
	static void PlaySequencesCmd(IConsoleCmdArgs *pArgs);
#endif

	void DoNodeStaticInitialisation();
private:
	ISystem*	m_system;

	IMovieUser *m_pUser;
	IMovieCallback *m_pCallback;

	CTimeValue m_lastUpdateTime;

	typedef std::vector<_smart_ptr<IAnimSequence> > Sequences;
	Sequences m_sequences;

	_smart_ptr<IAnimSequenceGroup> m_rootGroup;

	PlayingSequences m_playingSequences;

	typedef std::vector<IMovieListener*> TMovieListenerVec;
	typedef std::map<IAnimSequence*, TMovieListenerVec> TMovieListenerMap;

	// a container which maps sequences to all interested listeners
	// listeners is a vector (could be a set in case we have a lot of listeners, stl::push_back_unique!)
	TMovieListenerMap m_movieListenerMap;

	bool	m_bRecording;
	bool	m_bPaused;
	bool	m_bCutscenesPausedInEditor;
	int   m_iHudState;
	int		m_nProceduralAnimation;

	bool m_bLastFrameAnimateOnStop;

	SCameraParams m_ActiveCameraParams;

	ESequenceStopBehavior m_sequenceStopBehavior;

	bool m_bStartCapture;
	bool m_bEndCapture;
	ICaptureKey m_captureKey;
	float m_fixedTimeStepBackUp;
	ICVar *m_cvar_capture_file_format;
	ICVar *m_cvar_capture_frame_once;
	ICVar *m_cvar_capture_folder;
	ICVar *m_cvar_t_FixedStep;
	ICVar *m_cvar_capture_frames;
	ICVar *m_cvar_capture_file_prefix;
	ICVar *m_cvar_capture_misc_render_buffers;

	static int m_mov_NoCutscenes;
	ICVar *m_mov_overrideCam;

	bool m_bPhysicsEventsEnabled;

	bool m_bBatchRenderMode;

	// A sequence which turned on the early movie update last time
	IAnimSequence *m_pSeqWhichTurnedOnEMU;

	struct SkelAnimUpdateCMD
	{
		enum eType
		{
			MANUAL_SEEK,
			SET_LAYER_TIME,
			SET_WEIGHT,
			START_ANIM,
			STOP_ANIM,
		};

		ISkeletonAnim *pSkelAnim;
		eType type;
		uint32 layer;
		uint32 animIndex;
		float time;
		bool bAnimEvents;
		float weight;
		string animName;
		bool bLoop;

		SkelAnimUpdateCMD() 
			: pSkelAnim(NULL), type(MANUAL_SEEK), layer(0), animIndex(0), time(0), 
			bAnimEvents(false), weight(0), bLoop(false) {}
	};

	std::vector<SkelAnimUpdateCMD> m_skelAnimCmds;

	uint32 m_nextSequenceId;

	void ShowPlayedSequencesDebug();
public:
#if !defined(_RELEASE)
	static int m_mov_DebugEvents;
	static int m_mov_debugCamShake;
#endif
};

#endif // __movie_h__
